# -*- encoding: utf-8 -*-
import socket
import time
import requests
import uiautomator2

from loguru import logger
from apscheduler.schedulers.background import BackgroundScheduler

from settings import settings
from crawl.task import xhs_note
from utils.command import uiautomator2_init
from utils.public import http_session
from utils.proxy_droid import ProxyDroid


def task_pre(task_name: str, app_name=None, mitm_mode=True, allow_create_env=True, ):
    def decorator(function):
        def wrapper(self: 'Client', *args, **kwargs):
            result = self.update_mitm_proxy()
            if not result:
                logger.error(f"update mitm proxy error")
                return False
            result = self.restart_proxy_client()
            if not result:
                logger.info(f"restart proxy client error")
                return False
            logger.info(f"update mitm proxy success")
            return function(self, *args, **kwargs)

        return wrapper

    return decorator


class Client(object):
    def __init__(self):
        if not settings.RUN_TERMUX:
            logger.info('ui2 init ing....')
            uiautomator2_init()
            logger.info('ui2 init success')
        logger.info('__init__')
        self._drive = uiautomator2.connect_wifi("localhost")
        self._serial = self._drive.device_info["serial"]
        logger.info(self._serial)
        self.proxy_droid_client = ProxyDroid(self._drive)
        self._task_url = f'{settings.api_host}/android/task/{self._serial}'
        self._check_heartbeat_url = f'{settings.api_host}/android/heartbeat/{self._serial}'
        self._update_heartbeat_url = f'{settings.api_host}/android/{self._serial}'
        self.init_devices_timed_task()

    def init_devices_timed_task(self):
        scheduler = BackgroundScheduler()
        scheduler.add_job(self.heartbeat_server, trigger='interval', seconds=2)
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.bind(("127.0.0.1", 47200))
        except socket.error:
            logger.error(f"scheduler already started, DO NOTHING")
        else:
            logger.info(f"scheduler start")
            scheduler.start()

    def check_devices_exists_online(self):
        response = requests.get(self._check_heartbeat_url, timeout=5)
        data = response.json()
        if data['code'] == 1:
            return True
        return False

    def update_mitm_proxy(self, timeout=30):
        """
        更新中间人代理
        :return:
        """
        url = f"{settings.api_host}/android/mitm/{self._serial}/"
        params = {
            "timeout": timeout,
        }
        resp = http_session.post(url, params=params, timeout=(timeout + 5))
        if resp.json()["code"] == 1:
            return True
        return False

    def restart_proxy_client(self):
        self.proxy_droid_client.stop()
        time.sleep(2)
        result = self.proxy_droid_client.open_switch()
        if not result:
            logger.error(f"open proxy proxy droid global proxy failed")
            return False
        time.sleep(3)
        logger.info(f"open proxy proxy droid global proxy success")
        return True

    def heartbeat_server(self):
        data = {
            'current_app': self._drive.info['currentPackageName'],
            'version': '2021-08-05'
        }
        response = requests.post(self._update_heartbeat_url, json=data)
        if response.status_code != 200 and response.json()['code'] != 1:
            logger.error(f'{self._serial} heartbeat error')

    def set_task_execute_result(self, task_id, result):
        """
        设置任务执行结果
        :param task_id:  任务id
        :param result:  任务执行结果
        :return:
        """
        params = {
            'task_id': task_id,
            'result': result
        }
        response = requests.put(self._task_url, params=params, timeout=5)
        data = response.json()
        if data['code'] == 1:
            return True
        return False

    def get_task(self):
        response = requests.get(self._task_url, timeout=5)
        data = response.json()
        if data['code'] == 1:
            task = data['data']['task']
            return task
        return None

    @task_pre(
        task_name='xhs_note'
    )
    def xhs_note(self, note_id):
        print('in xhs_note')
        return xhs_note(self._drive, note_id)

    def callback(self, task: dict):
        task_id = task['task_id']
        args = task.get('args', ())
        kwargs = task.get('kwargs', {})
        func_name = task['task_name']
        function = getattr(self, func_name)
        logger.info(f'task_id: {task_id}, func_name: {func_name}')
        try:
            result = function(*args, **kwargs)
        except Exception as exc:
            print(exc)
            return
        logger.info(f'task_id: {task_id} completed！')
        self.set_task_execute_result(task_id, result)

    def run_task(self):
        while 1:
            time.sleep(3)
            devices_status = self.check_devices_exists_online()
            if devices_status is False:
                logger.warning('devices be not online')
                continue
            execution_result = self.get_task()
            if execution_result is None:
                continue
            self.callback(execution_result)
